use std::{
    collections::HashMap,
    f32::consts::LN_10,
    sync::{Arc, Mutex},
};

use model_graph_types::{
    connector::{Connector, ExecuteOptionsBuilder, QueryOptionsBuilder},
    container::workflow::{
        SqlStatement, WorkflowBlockExecuteResult, WorkflowBlockExecuteResultBuilder,
        WorkflowBlockExecuteResultStatus, WorkflowErrorStrategy,
    },
    modeling::{system_meta_database, Value, ValueType},
};
use union_connector::UnionConnector;

use crate::workflow::{
    blocks::statements::_utils::{datasource_by_id, query_value_with_variables},
    context::{Getter, WorkflowContext},
};

pub async fn execute(
    statement: &SqlStatement,
    context: Arc<Mutex<WorkflowContext>>,
) -> anyhow::Result<WorkflowBlockExecuteResult> {
    let error_strategy = statement.error_strategy.clone();

    let result = _execute(statement, context).await;
    tracing::debug!("sql statement结束:{:?}", result);
    match result {
        Ok(v) => Ok(WorkflowBlockExecuteResultBuilder::default()
            .status(WorkflowBlockExecuteResultStatus::Succeeded)
            .source_handle("source")
            .result(v)
            .build()?),
        Err(err) => {
            if let Some(error_strategy) = error_strategy {
                match error_strategy {
                    WorkflowErrorStrategy::FailBranch => {
                        Ok(WorkflowBlockExecuteResultBuilder::default()
                            .status(WorkflowBlockExecuteResultStatus::Succeeded)
                            .source_handle("fail-branch")
                            .build()?)
                    }
                    _ => Ok(WorkflowBlockExecuteResultBuilder::default()
                        .status(WorkflowBlockExecuteResultStatus::Succeeded)
                        .source_handle("source")
                        .build()?),
                }
            } else {
                Ok(WorkflowBlockExecuteResultBuilder::default()
                    .status(WorkflowBlockExecuteResultStatus::Succeeded)
                    .source_handle("source")
                    .build()?)
            }
        }
    }
}

async fn _execute(
    statement: &SqlStatement,
    context: Arc<Mutex<WorkflowContext>>,
) -> anyhow::Result<HashMap<String, Value>> {
    tracing::debug!("sql statement.....");
    //
    let script = query_value_with_variables(&statement.script, context.clone()).await?;
    //数据源
    let datasource = if let Some(datasource_selector) = &statement.datasource {
        if datasource_selector.1 != "" {
            //直接获取到变量
            let ds_value = context.get_from_node(&datasource_selector.1, &datasource_selector.0)?;
            match ds_value {
                Value::String(ds_str) => serde_json::from_str(&ds_str).map_err(|err| {
                    anyhow::anyhow!("参数数据库不是合法的字符串[{}]:{}", ds_str, err)
                })?,
                _ => system_meta_database()?,
            }
        } else {
            datasource_by_id(&Some(datasource_selector.0.clone()), context.clone()).await?
        }
    } else {
        datasource_by_id(&None, context.clone()).await?
    };
    //
    let connector = UnionConnector::open(&datasource).await?;
    let mut result: HashMap<String, Value> = HashMap::new();

    // 组装参数
    let args = vec![];

    if statement.is_query {
        let outputs = &statement.outputs;
        tracing::debug!("输出类型:[{:?}]", outputs);
        //查询
        if outputs.len() == 1 {
            let mut key = String::new();
            let mut value_type = ValueType::Object;

            for k in outputs.keys() {
                key = k.clone();
                if let Some(t) = outputs.get(k) {
                    value_type = t.clone();
                }
            }
            //
            match value_type {
                ValueType::Array => {
                    let res = connector
                        .query_objects(&script, &args, QueryOptionsBuilder::default().build()?)
                        .await?;
                    result.insert(key, Value::Array(res));
                }
                ValueType::Object => {
                    //通过返回值的类型，推断是使用那个进行执行
                    if let Ok(object) = connector
                        .query_object(&script, &args, QueryOptionsBuilder::default().build()?)
                        .await
                    {
                        result.insert(key, object);
                    } else {
                        //没有查询到结果
                        result.insert(key, Value::None);
                    }
                }
                _ => {
                    //
                    if let Ok(Value::Object(object)) = connector
                        .query_object(&script, &args, QueryOptionsBuilder::default().build()?)
                        .await
                    {
                        //
                        if let Some(v) = object.values().next() {
                            result.insert(key, v.convert(&value_type)?);
                        }
                    } else {
                        //没有查询到结果
                        result.insert(key, Value::None);
                    }
                }
            }
        } else {
            //需要取很多值
            if let Ok(Value::Object(object)) = connector
                .query_object(&script, &args, QueryOptionsBuilder::default().build()?)
                .await
            {
                //通过返回值的类型，推断是使用那个进行执行
                for (output_key, v_t) in outputs {
                    if let Some(v) = object.get(output_key) {
                        result.insert(output_key.clone(), v.convert(v_t)?);
                    }
                }
            } else {
                //没有查询到结果
                for (output_key, _) in outputs {
                    result.insert(output_key.clone(), Value::None);
                }
            }
        }
        //TODO 需要根据返回的类型进行判断
    } else {
        //执行
        let res = connector
            .execute(&script, &args, ExecuteOptionsBuilder::default().build()?)
            .await?;
        result.insert(String::from("result"), Value::Number(res));
    }
    Ok(result)
}
